#pragma once
#include<unistd.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include"../comm/spdlog.hpp"
#include"../comm/util.hpp"
namespace y_runner
{
    using namespace y_util;

    class Runner
    {
        public:
        Runner(){}
        ~Runner(){}

    public:
        static int Run(const std::string& file_name,int cpu_limit,int mem_limit)
        {
            std::string _exe=PathUtil::Exe(file_name);
            std::string _stdin=PathUtil::Stdin(file_name);
            std::string _stdout=PathUtil::Stdout(file_name);
            std::string _stderr=PathUtil::Stderr(file_name);

            umask(0);
            int _stdin_fd=open(_stdin.c_str(),O_CREAT|O_RDONLY,0644);
            int _stdout_fd=open(_stdout.c_str(),O_CREAT|O_WRONLY,0644);
            int _stderr_fd=open(_stderr.c_str(),O_CREAT|O_WRONLY,0644);

            if(_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                ERROR("运行时打开标准文件失败");
                return -1; //代表打开文件失败
            }
            
            pid_t pid=fork();
            if(pid<0)
            {
                ERROR("运行时创建子进程失败");
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                return -2;//表示子进程创建失败
            }
            else if(pid==0)
            {
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);

                //设置空间复杂度和时间复杂度（也就是设置CPU时长和内存占用大小）
                SetProcLimit(cpu_limit,mem_limit);

                //运行可执行文件
                execl(_exe.c_str(),_exe.c_str(),nullptr);
                exit(1);
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status=0;
                waitpid(pid,&status,0);
                INFO("运行完毕，信号为：{}",status&0x7F);
                return status&0x7F;
            }
        }
    private:
        static void SetProcLimit(int _cpu_limit,int _mem_limit)
        {
            //设置CPU时长
            struct rlimit cpu_limit;
            /*将cpu_limit.rlim_max设置为RLIM_INFINITY，表示将CPU时间的硬限制设置为无限。
             *硬限制是管理员或系统设置的上限，普通用户不能超过这个限制。*/
            cpu_limit.rlim_max=RLIM_INFINITY;
            /*将cpu_limit.rlim_cur设置为_cpu_limit，表示将CPU时间的软限制设置为_cpu_limit的值。
             *软限制是当前生效的限制，当进程达到这个限制时，
             *会收到一个信号（通常是SIGXCPU），如果进程没有处理这个信号，就会被终止。*/
            cpu_limit.rlim_cur=_cpu_limit;
            setrlimit(RLIMIT_CPU,&cpu_limit);

            //设置内存大小
            struct rlimit mem_limit;
            mem_limit.rlim_max=RLIM_INFINITY;
            mem_limit.rlim_cur=_mem_limit*1024;
            setrlimit(RLIMIT_AS,&mem_limit);
        }
    };
}